package upm.pb4agent.team3;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.EnteringDock;
import upm.pb4agent.ontology.LeavingDock;
import upm.pb4agent.ontology.PB4AgentOntology;
import upm.pb4agent.ontology.RaceFinished;
import upm.pb4agent.ontology.RaceStarted;
import upm.pb4agent.ontology.SubscribeReferee;


/**
 * This agent has the following functionality: 
 * <ul>
 * <li> 
 * <li> 
 * </ul>
 * @author Team3, UPM
 */
public class SubscriptionResponderReferee extends CyclicBehaviour {
	private static final long serialVersionUID = 8509580268336553303L;
	
	private Codec codec = new SLCodec();
	private Ontology ontology = PB4AgentOntology.getInstance();
	
	private Vector<AID> subscriptores = new Vector<AID>(0);
	private Vector<RaceFinished> raceOrderArrives = new Vector<RaceFinished>(0);
	
	private Long timeStartRace;
	private Long timeOutRace;
	public SubscriptionResponderReferee(Agent agent) {
		super(agent); 
		super.myAgent.getContentManager().registerLanguage(codec);
		super.myAgent.getContentManager().registerOntology(ontology);
		
		ReadConfiguration.getInstance(0);
		String registrationPeriodStr = ReadConfiguration.getValue("registration_period");		
		long registrationPeriodProp = Long.valueOf(registrationPeriodStr);
		timeOutRace = registrationPeriodProp * 1;
    }
	
	
	public void action(){
		/*ACLMessage aclMessage = super.myAgent.receive( MessageTemplate.or(
														        		   MessageTemplate.and( MessageTemplate.MatchLanguage(new jade.content.lang.sl.SLCodec().getName())
														        		 					   ,MessageTemplate.MatchOntology(PB4AgentOntology.getInstance().getName())
														        		 					   )														        		 			
												        		 			,MessageTemplate.or(
												        							  		MessageTemplate.or(  MessageTemplate.or(  MessageTemplate.MatchPerformative(ACLMessage.SUBSCRIBE)	                                                                  
																							        						  		 ,MessageTemplate.MatchPerformative(ACLMessage.CANCEL)
																							        						  		)											        					
																							        		    ,MessageTemplate.MatchPerformative(ACLMessage.INFORM)
																							        		   )                               
																							,MessageTemplate.MatchProtocol("SubscribeReferee")
																						    )
																		)
																							  
														);*/
		
		List<Integer> performativeList = new ArrayList<Integer>(3);
		performativeList.add(ACLMessage.SUBSCRIBE);
		performativeList.add(ACLMessage.INFORM);		
		performativeList.add(ACLMessage.CANCEL);
		List<String> protocolList = new ArrayList<String>(4);
		protocolList.add("SubscribeReferee");
		protocolList.add("EnteringDock");
		protocolList.add("LeavingDock");
		protocolList.add("RaceFinished");
		protocolList.add("NotifyRaceStart");
		
		MessageTemplate mt = MyAgent.addMessageTemplatOROR(performativeList, protocolList);		
		ACLMessage aclMessage =  super.myAgent.receive(mt);
		
		if(aclMessage == null){
			block();
		}else{
			
			switch(aclMessage.getPerformative()) {			
				case ACLMessage.SUBSCRIBE:				
					handleSubscription(aclMessage);	
					break;
				case ACLMessage.CANCEL:
					handleCancel(aclMessage);
					break; 
				case ACLMessage.INFORM:					
					handleInform(aclMessage);
					break;
				default:
					handleRefuse(aclMessage);	
					break;
			}
		}
		

	}

	
    protected ACLMessage handleSubscription(ACLMessage subscription_msg) {
        // handle a subscription request
    	try{
			ACLMessage reply = subscription_msg.createReply();

			try{
				ContentElement contentElement = super.myAgent.getContentManager().extractContent(subscription_msg);
				if (contentElement instanceof Action){
					Action action = (Action) contentElement;
					Concept concept = action.getAction();
					if(concept instanceof SubscribeReferee){
						subscriptores.add(subscription_msg.getSender());
													
						reply.setPerformative(ACLMessage.AGREE);
						myAgent.send(reply);
						
						System.out.println(myAgent.getLocalName() + " --> " + subscription_msg.getSender().getLocalName()  + ", has been subscribed");
						
					}
				}
			}catch (Exception e) {			
				reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
				reply.setContent("Message hasn't appropriate content.");
				myAgent.send(reply);
				System.out.println(myAgent.getLocalName() + ":"  + ", not understood");
			}	
			//subscription_msg = null;					
			
			return subscription_msg;
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}
        
    }
    
    
	
	protected ACLMessage handleCancel(ACLMessage cancel_msg) {
        // handle a subscription request
    	try{			
			ACLMessage reply = cancel_msg.createReply();
			
			// Wait for a subscription request
			reply.setPerformative(ACLMessage.INFORM);
			reply.setContent("Subscription canceled");
			subscriptores.remove(cancel_msg.getSender());
			
			//System.out.println(myAgent.getLocalName() + ": " + cancel_msg.getSender().getLocalName()  + ", has been unsubscribed");
			myAgent.send(reply);

			return cancel_msg;
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}
        
    }
	     
    
    protected ACLMessage handleInform(ACLMessage subscription_msg) {
        // handle a subscription request
    	try{    		
			//ACLMessage reply = subscription_msg.createReply();			
			//subscription_msg = null;
			if(subscription_msg.getProtocol().equals("NotifyRaceStart")){
				notifyRaceStarted();
			}else{
				ContentElement contentElement = super.myAgent.getContentManager().extractContent(subscription_msg);
				if(contentElement instanceof Action) {
					Action action = (Action) contentElement;
					Concept concept = action.getAction();																							
					if(concept instanceof EnteringDock) {
						EnteringDock enteringDock = (EnteringDock)concept;
						notifyEnteringDock(enteringDock);
					}else if(concept instanceof LeavingDock) {
						LeavingDock leavingDock = (LeavingDock)concept;
						notifyLeavingDock(leavingDock);
					}else if(concept instanceof RaceFinished) {							
						RaceFinished raceFinished = (RaceFinished)concept;
						//Long deltaTime = Long.valueOf(raceFinished.getTimeStamp()) - timeStartRace;
						//raceFinished.setTimeStamp(Long.toString(deltaTime));
						//Long deltaTime = System.currentTimeMillis() - timeStartRace;
						//raceFinished.setTimeStamp(Long.toString(deltaTime));
						raceOrderArrives.add(raceFinished);
						raceFinished.setPosition(raceOrderArrives.size());
						printRaceOrderArrives(raceFinished);
						notifyRaceFinished(raceFinished);
						
						/*System.out.println("RaceFinishedPerTeam");
						System.out.println("System.currentTimeMillis() " + System.currentTimeMillis());
						System.out.println("startRaceTime " + startRaceTime);
						System.out.println("timeOutRaceTime " + timeOutRaceTime);
						System.out.println("(System.currentTimeMillis() - startRaceTime) " + (System.currentTimeMillis() - startRaceTime));
						raceOrderArrives.add(raceFinished);
						if(raceOrderArrives.size() == Constant.MAX_NUMBER_OF_BOATS
							|| (System.currentTimeMillis() - startRaceTime) >= timeOutRaceTime ){					
							notifyRaceFinished(raceFinished);
							printRaceOrderArrives();
						}*/
					}
				}
			}
			return subscription_msg;
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}
        
    }
    
    public void printRaceOrderArrives(RaceFinished raceFinished){    	
    	StringBuffer positions = new StringBuffer();

		positions.append("-::--::--::--::--::--::--::--::--::--::-\n");
		positions.append("ORDER OF ARRIVALS:");
		positions.append(" <Boat: '" + raceFinished.getBoatAID().getLocalName() + "'>");
		
		positions.append(" <Position: '" + raceFinished.getPosition() + "'>");
		try{
			Date dateStartRace = new Date(this.timeStartRace);
			positions.append(" <Time 'StartRace,FinishRace' (hh:mi:ss): '" +  dateStartRace.getHours() + ":" + dateStartRace.getMinutes() + ":" +  dateStartRace.getSeconds());
			Date arrivalDate = new Date(Long.valueOf(raceFinished.getTimeStamp()));
			positions.append(" , " +  arrivalDate.getHours() + ":" + arrivalDate.getMinutes() + ":" +  arrivalDate.getSeconds() + "'>");
					
		}catch (Exception e) {
			e.printStackTrace();
			positions.append(" <Time: '" + raceFinished.getTimeStamp() + "'>");
		}
		
		positions.append("\n-::--::--::--::--::--::--::--::--::--::-");

    	System.out.println(positions.toString());
    }
    
    public void printRaceOrderArrives(){
    	RaceFinished raceFinished = null;
    	StringBuffer positions = new StringBuffer();
    	for(int i=0; i<raceOrderArrives.size();i++){
    		positions.append("-::--::--::--::--::--::--::--::--::--::-");
    		positions.append("ORDER OF ARRIVES:");
    		raceFinished = raceOrderArrives.get(i);
    		positions.append(" <Boat: " + raceFinished.getBoatAID().getLocalName() + ">");
    		Date date = new Date(raceFinished.getTimeStamp());
    		positions.append(" <Position: " + i + ">");
    		positions.append(" <Time: " + date.getMinutes() + ":" +  date.getMinutes() + ">");
    		positions.append("-::--::--::--::--::--::--::--::--::--::-");
    	}
    	System.out.println(positions.toString());
    }
    
    
    protected ACLMessage handleRefuse(ACLMessage subscription_msg) {
    	//otherwise with REFUSE
    	try{
			ACLMessage reply = subscription_msg.createReply();
			reply.setPerformative(ACLMessage.REFUSE);
			reply.setContent("I don't handle this performative");
			myAgent.send(reply);
			System.out.println(myAgent.getLocalName() + ":"  + ", I don't handle this performative ::: " + subscription_msg);
			subscription_msg = null;
			
			return subscription_msg;
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}        
    }
    

    protected void notifyRaceStarted() {
    	try{
    		timeStartRace = System.currentTimeMillis();
    		
    		
    		ACLMessage msgInform = new ACLMessage(ACLMessage.INFORM);    	
	    	msgInform.setLanguage(codec.getName());
	    	msgInform.setOntology(ontology.getName());  
	    		    	
	    	Action agAction = new Action();
        	RaceStarted raceStarted = new RaceStarted();        	
        	agAction.setAction(raceStarted);
        	agAction.setActor(super.myAgent.getAID());
        	super.myAgent.getContentManager().fillContent(msgInform, agAction);
            
            msgInform.setProtocol("RaceStarted");
        	for(int i=0; i<subscriptores.size(); i++){
        		msgInform.addReceiver(subscriptores.get(i));
        	}
        	myAgent.send(msgInform);
        	System.out.println(super.myAgent.getLocalName() + " -->> notifyRaceStarted, good luck!!!");
        	System.out.println("");	        
        	System.out.println("");
        	System.out.println("");
    	}catch (Exception e) {
    		System.out.println(super.myAgent.getLocalName() + ".notifyRaceStarted(), error " + e.toString());
		}
    }
    
    protected void notifyRaceFinished(RaceFinished raceFinished) {
    	try{
    		ACLMessage msgInform = new ACLMessage(ACLMessage.INFORM);    	
	    	msgInform.setLanguage(codec.getName());
	    	msgInform.setOntology(ontology.getName());  
	    		    	
	    	Action actionRaceFinished = new Action();
        	//RaceFinished raceFinished = new RaceFinished();   
        	
        	/*upm.pb4agent.ontology.Boat boat = new upm.pb4agent.ontology.Boat();
        	raceFinished.setBoatAID(boat.getID());        	
        	raceFinished.setPosition(1);
        	long deltaTime = this.startRaceTime - System.currentTimeMillis();
        	String deltaTimeStr = String.valueOf(deltaTime);*/
        	
        	actionRaceFinished.setAction(raceFinished);
        	actionRaceFinished.setActor(super.myAgent.getAID());
        	super.myAgent.getContentManager().fillContent(msgInform, actionRaceFinished);
            
            msgInform.setProtocol("RaceFinished");
        	for(int i=0; i<subscriptores.size(); i++){
        		msgInform.addReceiver(subscriptores.get(i));
        	}
        	myAgent.send(msgInform);
        	//System.out.println("Inform + subscriptores.size():  " + msgInform);
	        
    	}catch (Exception e) {
    		System.out.println(super.myAgent.getLocalName() + ".notifyRaceFinished(), error " + e.toString());
		}
    }
    
    protected void notifyEnteringDock(EnteringDock enteringDock) {
    	try{
    		ACLMessage msgInform = new ACLMessage(ACLMessage.INFORM);    	
	    	msgInform.setLanguage(codec.getName());
	    	msgInform.setOntology(ontology.getName());  
	    		    	
        	//EnteringDock enteringDock = new EnteringDock();
        	
        	/*upm.pb4agent.ontology.Boat boat = new upm.pb4agent.ontology.Boat();
        	enteringDock.setBoatAID(boat.getID());
        	Dock dock = new Dock();
        	enteringDock.setDock(dock);
        	enteringDock.setPosition(1);
        	long deltaTime = this.startRaceTime - System.currentTimeMillis();
        	String deltaTimeStr = String.valueOf(deltaTime);        	        
        	enteringDock.setTimeStamp(deltaTimeStr);*/       	
        	
	    	Action actionEnteringDock = new Action();
        	actionEnteringDock.setAction(enteringDock);
        	actionEnteringDock.setActor(super.myAgent.getAID());
        	super.myAgent.getContentManager().fillContent(msgInform, actionEnteringDock);
            
            msgInform.setProtocol("EnteringDock");
        	for(int i=0; i<subscriptores.size(); i++){
        		msgInform.addReceiver(subscriptores.get(i));
        	}
        	myAgent.send(msgInform);
        	//System.out.println("Inform + subscriptores.size():  " + msgInform);
	        
    	}catch (Exception e) {
    		System.out.println(super.myAgent.getLocalName() + ".notifyEnteringDock(), error " + e.toString());
		}
    }
    
    protected void notifyLeavingDock(LeavingDock leavingDock){
    	try{
    		ACLMessage msgInform = new ACLMessage(ACLMessage.INFORM);    	
	    	msgInform.setLanguage(codec.getName());
	    	msgInform.setOntology(ontology.getName());  
	    		    	
	    	Action actionLeavingDock = new Action();
        	/*LeavingDock leavingDock = new LeavingDock(); 
        	
        	upm.pb4agent.ontology.Boat boat = new upm.pb4agent.ontology.Boat();
        	leavingDock.setBoatAID(boat);        	
        	Dock dock = new Dock();
        	leavingDock.setDock(dock);*/
        	
	    	actionLeavingDock.setAction(leavingDock);
	    	actionLeavingDock.setActor(super.myAgent.getAID());
        	super.myAgent.getContentManager().fillContent(msgInform, actionLeavingDock);
            
            msgInform.setProtocol("LeavingDock");
        	for(int i=0; i<subscriptores.size(); i++){
        		msgInform.addReceiver(subscriptores.get(i));
        	}
        	myAgent.send(msgInform);
        	//System.out.println("Inform + subscriptores.size():  " + msgInform);
	        
    	}catch (Exception e) {
    		System.out.println(super.myAgent.getLocalName() + ".notifyLeavingDock(), error " + e.toString());
		}
    }
}